/*!
 * @file particle.cpp
 * @brief Ciało particle.hh
 */

#include "engine/basic/particle.hh"
using namespace std;

/*--------------------------------------------------------------------------*/

particle::particle(point3f pstatic, point3f vector, string cfg) {

	this->position = pstatic;
	this->vector = vector;
	this->vector.normalize();

	this->cfg = new inifile(cfg);

	/*
	 * Ustawianie zmiennych z pliku konfiguracyjnego
	 */
	this->maxDust = this->cfg->getInteger("emitter", "maxDust", 10);

	this->maxDist = this->cfg->getFloat("emitter", "maxDist", 100.0f);
	this->fadeoutDist = this->cfg->getFloat("emitter", "fadeoutDist", 100.0f);

	this->emmisionProbability = this->cfg->getInteger("emitter",
			"emmisionProbability", 20);

	this->sizeAtBegin = this->cfg->getFloat("emitter", "sizeAtBegin", 0.1f);
	this->sizeAtEnd = this->cfg->getInteger("emitter", "sizeAtEnd", 20.0f);

	//todo sinus cosinus gdzieś trza dać minus...
	this->spread = this->cfg->getInteger("emitter", "spread", 25.0f);

	this->dustColor.r = this->cfg->getFloat("emitter", "Rgb", 1.0f);
	this->dustColor.g = this->cfg->getFloat("emitter", "rGb", 1.0f);
	this->dustColor.b = this->cfg->getFloat("emitter", "rgB", 1.0f);
	this->deltaRGB = this->cfg->getFloat("emitter", "deltaRGB", 0.0f);

	this->texCount = this->cfg->getInteger("textures", "texCount", 1);
	this->tex = new texture*[this->texCount];

	for (int i = 0; i < this->texCount; i++) {
		stringstream ss;
		ss << "tex" << i + 1;

		this->tex[i] = new texture(this->cfg->getString(
				"textures", ss.str(), "pawirr/textures/replaceme.png"));
	}

	this->pdust_stack = new list<__particleDust *> ;
	pawirr::graphic->_pcs->addParticle(this);
}

/*--------------------------------------------------------------------------*/

particle::~particle() {
	pawirr::graphic->_pcs->removeParticle(this);
}

/*--------------------------------------------------------------------------*/

void particle::calculate() {

	if (this->pdust_stack->size() < this->maxDust && rand() % 100
			< this->emmisionProbability) {

		__particleDust *n = new __particleDust;
		n->pos = this->position;

		n->vec = this->vector;

		if (this->spread != 0) {
			n->vec.x = n->vec.x + sin(2.0f * M_PI * (float) (rand()
					% this->spread) / 100.0f);
			n->vec.y = n->vec.y + sin(2.0f * M_PI * (float) (rand()
					% this->spread) / 100.0f);
			n->vec.z = n->vec.z + sin(2.0f * M_PI * (float) (rand()
					% this->spread) / 100.0f);
		}

		n->vec.normalize();

		n->liveProgress = 0.0f;
		n->cl = this->dustColor;

		float randomm = ((float) (rand() % 100)) / 100.0f;

		n->cl.r = this->dustColor.r - this->deltaRGB + this->deltaRGB * 2
				* randomm;
		n->cl.g = this->dustColor.g - this->deltaRGB + this->deltaRGB * 2
				* randomm;
		n->cl.b = this->dustColor.b - this->deltaRGB + this->deltaRGB * 2
				* randomm;

		n->cl.normalize();

		n->texNumber = rand() % this->texCount;

		this->pdust_stack->push_back(n);
	}

	float length;
	float dist;
	for (list<__particleDust *>::iterator i = this->pdust_stack->begin(); i
			!= this->pdust_stack->end(); i++) {

		length = (this->position - (*i)->pos).length();
		dist = this->maxDist + this->fadeoutDist;

		if (length > dist) {
			//zwalnianie pamięci
			delete (*i);
			//usuwanie z listy
			this->pdust_stack->erase(i);
			//cofnięcie iteratora
			i--;
		} else {
			(*i)->pos = (*i)->pos + (*i)->vec;

			(*i)->liveProgress = length / dist;

			if (length > this->maxDist) {
				(*i)->cl.a = 1.0f - (length - this->maxDist)
						/ this->fadeoutDist;
			} else {
				(*i)->cl.a = 1.0f;
			}
		}

	}

}

/*--------------------------------------------------------------------------*/

void particle::paint() {

	glEnable(GL_TEXTURE_2D);
	glPushMatrix();
	glDepthMask(GL_FALSE);

	float sx, sz, scale, rad;
	point3f camvec;

	/*
	 * Malowanie po kolei każdego rodzaju cząstek [wg tekstur]
	 */
	for (int ti = 0; ti < this->texCount; ti++) {

		glBindTexture(GL_TEXTURE_2D, this->tex[ti]->gltex);
		glBegin(GL_QUADS);

		for (list<__particleDust *>::iterator i = this->pdust_stack->begin(); i
				!= this->pdust_stack->end(); i++) {

			//przerywanie malowania cząstki jeśli inna tekstura jest w toku
			if ((*i)->texNumber != ti)
				continue;

			/*
			 * Obiczanie obrotu cząstki
			 */
			//Kamera - pozycja cząstki
			camvec = pawirr::graphic->_env->cameraPos - (*i)->pos;
			camvec.normalize();

			//Obliczanie radianów
			rad = asin(camvec.z / sqrt(camvec.x * camvec.x + camvec.z
					* camvec.z));

			//Czary mary, żeby wyszła funkcja ciągła przy pełnym obrocie [0-360stopni]
			if (rad < 0)
				rad *= -1;
			if (camvec.x <= 0.0f)
				rad = M_PI - rad;
			if (camvec.z <= 0.0f)
				rad = 2 * M_PI - rad;

			//bok cząstki
			scale = (this->sizeAtEnd - this->sizeAtBegin) * (*i)->liveProgress
					+ this->sizeAtBegin;

			//współrzędne przesuniętego wierzchołka
			sx = scale * cos(rad) - scale * sin(rad);
			sz = scale * sin(rad) + scale * cos(rad);

			glColor4f((*i)->cl.r, (*i)->cl.g, (*i)->cl.b, (*i)->cl.a);

			//TODO odwracanie zgodnie z kamerą
			glTexCoord2f(0.0f, 0.0f);
			glVertex3f((*i)->pos.x, (*i)->pos.y, (*i)->pos.z);

			glTexCoord2f(0.0f, 1.0f);
			glVertex3f((*i)->pos.x + sx, (*i)->pos.y, (*i)->pos.z + sz);

			glTexCoord2f(1.0f, 1.0f);
			glVertex3f((*i)->pos.x + sx, (*i)->pos.y + scale, (*i)->pos.z + sz);

			glTexCoord2f(1.0f, 0.0f);
			glVertex3f((*i)->pos.x, (*i)->pos.y + scale, (*i)->pos.z);

		}
		glEnd();
	}

	glDepthMask(GL_TRUE);
	glPopMatrix();
	glDisable(GL_TEXTURE_2D);
}

/*--------------------------------------------------------------------------*/
